﻿#include "audio.h"

AudioPlayer::AudioPlayer(QObject *parent) : QThread(parent)
{
    audioOutput = nullptr;
    isRun = false;
    runState = false;
}

AudioPlayer::~AudioPlayer()
{
    quit();
    wait();
}

void AudioPlayer::setAudioParm(PlayMode mode, const QString &url, int sampleRate, int sampleSize, int channel)
{
    this->mode = mode;
    this->playUrl = url;
    this->sampleRate = sampleRate;
    this->sampleSize = sampleSize/8;
    this->channel = channel;
}

void AudioPlayer::startPlayPcmFile()
{
    QFile pcmFile(playUrl);
    if(!pcmFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "open pcmFile error";
        return;
    }

    qDebug() << "Pcm Start";

    QAudioFormat format;
    format.setSampleRate(sampleRate);
    format.setChannelCount(channel);
    format.setSampleSize(sampleSize*8);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(format))
    {
        qWarning() << "Raw audio format not supported";
        return;
    }

    //一帧音频数据大小
    int inPcmSize = sampleSize * channel * 1024;
    char *inPcmBuf = new char[inPcmSize];

    //初始化输出
    if(audioOutput)
        delete audioOutput;

    audioOutput = new QAudioOutput(format);
    audioOutput->setBufferSize(inPcmSize * 10);
    QIODevice *audioIO = audioOutput->start();

    //音频数据缓存
    RingBuffer ringBuffer;
    ringBuffer.initBuffer(inPcmSize * 30);

    //数据读取线程
    FileThread fileThread;
    fileThread.ringBuffer = &ringBuffer;
    fileThread.pcmFile = &pcmFile;
    fileThread.pcmSize = inPcmSize;
    fileThread.readFinish = false;
    fileThread.isRun = true;
    fileThread.start();

    runState = true;
    while(isRun)
    {
        int bytesFree = audioOutput->bytesFree();
        if(bytesFree < inPcmSize)
        {
            QThread::msleep(1);
            continue;
        }

        int readLen = ringBuffer.readBuffer(inPcmBuf, inPcmSize);
        audioIO->write(inPcmBuf, readLen);

        if(fileThread.readFinish && ringBuffer.getReadableLen() == 0)
            break;
    }

    fileThread.isRun = false;

    delete[] inPcmBuf;
    inPcmBuf = nullptr;
    pcmFile.close();

    runState = false;
    qDebug() << "Pcm Finish";
}

void AudioPlayer::startPlayDevice()
{
    qDebug() << "Device Start";

    QAudioFormat format;
    format.setSampleRate(sampleRate);
    format.setChannelCount(channel);
    format.setSampleSize(sampleSize*8);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(format))
    {
        qWarning() << "Raw audio format not supported";
        return;
    }

    //一帧音频数据大小
    int inPcmSize = sampleSize * channel * 1024;
    char *inPcmBuf = new char[inPcmSize];

    //初始化输出
    if(audioOutput)
        delete audioOutput;

    audioOutput = new QAudioOutput(format);
    audioOutput->setBufferSize(inPcmSize * 10);
    QIODevice *audioIO = audioOutput->start();

    RingBuffer ringBuffer;
    ringBuffer.initBuffer(inPcmSize * 30);

    SoundTouchEx soundTouchEx;
    soundTouchEx.setProcessParm(sampleRate, channel, sampleSize, 1.0, 0.8);

    DeviceThread deviceThread;
    deviceThread.soundTouchEx = &soundTouchEx;
    deviceThread.ringBuffer = &ringBuffer;
    deviceThread.sampleRate = sampleRate;
    deviceThread.sampleSize = sampleSize;
    deviceThread.channel = channel;
    deviceThread.readFinish = false;
    deviceThread.isRun = true;
    deviceThread.start();

    QFile outPcmFile("./out.pcm");
    outPcmFile.open(QIODevice::WriteOnly);


    runState = true;
    while(isRun)
    {

#if 0
        //写入文件
        if(soundTouchEx.buffer.getReadableLen() < inPcmSize)
        {
            QThread::msleep(1);
            continue;
        }

        int readLen = soundTouchEx.buffer.readBuffer(inPcmBuf, inPcmSize);
        outPcmFile.write(inPcmBuf, readLen);

#else
        //实时变声
        int bytesFree = audioOutput->bytesFree();
        if(bytesFree < inPcmSize)
        {
            QThread::msleep(1);
            continue;
        }

        int readLen = ringBuffer.readBuffer(inPcmBuf, inPcmSize);
        audioIO->write(inPcmBuf, readLen);

#endif

        if(deviceThread.readFinish && ringBuffer.getReadableLen() == 0)
            break;
    }

    deviceThread.isRun = false;

    delete[] inPcmBuf;
    inPcmBuf = nullptr;

    runState = false;
    qDebug() << "Device Finish";
}

void AudioPlayer::startPlay()
{
    isRun = true;
    this->start();
}

void AudioPlayer::stopPlay()
{
    isRun = false;
}

bool AudioPlayer::isRunState()
{
    return runState;
}

void AudioPlayer::run()
{
    if(mode == PlayMode::PcmFile)
        startPlayPcmFile();
    else if(mode == PlayMode::Device)
        startPlayDevice();
}


FileThread::FileThread(QObject *parent) : QThread(parent)
{
    isRun = false;
    readFinish = false;
}

FileThread::~FileThread()
{
    quit();
    wait();
}

void FileThread::run()
{
    char *inPcmBuf = new char[pcmSize];

    while (isRun)
    {
        if(ringBuffer->getReadableLen() > pcmSize*20)
        {
            QThread::msleep(1);
            continue;
        }

        int readLen = pcmFile->read(inPcmBuf, pcmSize);
        ringBuffer->writeBuffer(inPcmBuf, readLen);

        if(readLen == 0)
        {
            readFinish = true;
            break;
        }
    }

    delete[] inPcmBuf;
    inPcmBuf = nullptr;
}

DeviceThread::DeviceThread(QObject *parent) : QThread(parent)
{
    audioInput = nullptr;
    isRun = false;
    readFinish = false;
}

DeviceThread::~DeviceThread()
{

}

void DeviceThread::run()
{
    //设置Qt音频录制参数
    QAudioFormat recordFmt;
    recordFmt.setSampleRate(sampleRate);
    recordFmt.setChannelCount(channel);
    recordFmt.setSampleSize(sampleSize * 8);
    recordFmt.setCodec("audio/pcm");
    recordFmt.setByteOrder(QAudioFormat::LittleEndian);
    recordFmt.setSampleType(QAudioFormat::UnSignedInt);
    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    if (!info.isFormatSupported(recordFmt))
    {
        qDebug() << "Audio format not support!";
        recordFmt = info.nearestFormat(recordFmt);
    }

    //设置采集音频接口
    if(audioInput)
        delete audioInput;

    audioInput = new QAudioInput(recordFmt);
    QIODevice *audioIO = audioInput->start();

    int pcmSize = sampleSize * channel * 1024;
    char *inPcmBuf = new char[pcmSize];

    while (isRun)
    {
        if (audioInput->bytesReady() >= pcmSize)
        {
            int readLen = audioIO->read(inPcmBuf, pcmSize);
            soundTouchEx->processAudioData(inPcmBuf, readLen);

            if(readLen == 0)
            {
                readFinish = true;
                break;
            }
        }
    }

    delete[] inPcmBuf;
    inPcmBuf = nullptr;
}
